home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 8: LINUX Games / Linux Cubed Series 8 - LINUX Games.iso / games / actionrp / nhplusx.bin / nhplusx / nhplusX / X11 / winmesg.c < prev    next >
C/C++ Source or Header  |  1995-09-10  |  17KB  |  618 lines

  1. /*    SCCS Id: @(#)winmesg.c    3.1    93/02/02          */
  2. /* Copyright (c) Dean Luick, 1992                  */
  3. /* NetHack may be freely redistributed.  See license for details. */
  4.  
  5. /*
  6.  * Message window routines.
  7.  *
  8.  * Global functions:
  9.  *    create_message_window()
  10.  *    destroy_message_window()
  11.  *    display_message_window()
  12.  *    append_message()
  13.  */
  14.  
  15. #ifndef SYSV
  16. #define PRESERVE_NO_SYSV    /* X11 include files may define SYSV */
  17. #endif
  18.  
  19. #include <X11/Intrinsic.h>
  20. #include <X11/StringDefs.h>
  21. #include <X11/Shell.h>
  22. #include <X11/Xaw/Cardinals.h>
  23. #include <X11/Xaw/Viewport.h>
  24. #include <X11/Xatom.h>
  25.  
  26. #ifdef PRESERVE_NO_SYSV
  27. # ifdef SYSV
  28. #  undef SYSV
  29. # endif
  30. # undef PRESERVE_NO_SYSV
  31. #endif
  32.  
  33. #include "Window.h"    /* Window widget declarations */
  34.  
  35. #include "hack.h"
  36. #include "winX.h"
  37.  
  38. static struct line_element *FDECL(get_previous, (struct line_element *));
  39. static void FDECL(set_circle_buf, (struct mesg_info_t *,int));
  40. static char *FDECL(split, (char *,XFontStruct *,DIMENSION_P));
  41. static void FDECL(add_line, (struct mesg_info_t *,const char *));
  42. static void FDECL(redraw_message_window, (struct xwindow *));
  43. static void FDECL(mesg_check_size_change, (struct xwindow *));
  44. static void FDECL(mesg_exposed, (Widget,XtPointer,XtPointer));
  45. static void FDECL(get_gc, (Widget,struct mesg_info_t *));
  46. static void FDECL(mesg_resized, (Widget,XtPointer,XtPointer));
  47.  
  48. /* Move the message window's vertical scrollbar's slider to the bottom. */
  49. void
  50. set_message_slider(wp)
  51.     struct xwindow *wp;
  52. {
  53.     Widget scrollbar;
  54.     float top;
  55.  
  56.     scrollbar = XtNameToWidget(XtParent(wp->w), "vertical");
  57.  
  58.     if (scrollbar) {
  59.     top = 1.0;
  60.     XtCallCallbacks(scrollbar, XtNjumpProc, &top);
  61.     }
  62. }
  63.  
  64.  
  65. void
  66. create_message_window(wp, create_popup, parent)
  67.     struct xwindow *wp;            /* window pointer */
  68.     boolean create_popup;
  69.     Widget parent;
  70. {
  71.     Arg args[8];
  72.     Cardinal num_args;
  73.     Widget viewport;
  74.     struct mesg_info_t *mesg_info;
  75.  
  76.     wp->type = NHW_MESSAGE;
  77.  
  78.     wp->mesg_information = mesg_info =
  79.             (struct mesg_info_t *) alloc(sizeof(struct mesg_info_t));
  80.  
  81.     mesg_info->fs = 0;
  82.     mesg_info->num_lines = 0;
  83.     mesg_info->head = mesg_info->line_here = mesg_info->last_pause =
  84.             mesg_info->last_pause_head = (struct line_element *) 0;
  85.     mesg_info->dirty = False;
  86.     mesg_info->viewport_width = mesg_info->viewport_height = 0;
  87.  
  88.     /*
  89.      * We should have an .Xdefaults option that specifies the number of lines
  90.      * to be displayed.  Until then, we'll use DEFAULT_LINES_DISPLAYED.
  91.      * E.g.:
  92.      *
  93.      *    if (a lines value from .Xdefaults exists)
  94.      *        lines_displayed = lines value from .Xdefaults;
  95.      *    else
  96.      *        lines_displayed = DEFAULT_LINES_DISPLAYED;
  97.      */
  98.     if (flags.msg_history < DEFAULT_LINES_DISPLAYED)
  99.     flags.msg_history = DEFAULT_LINES_DISPLAYED;
  100.     if (flags.msg_history > MAX_HISTORY)    /* a sanity check */
  101.     flags.msg_history = MAX_HISTORY;
  102.  
  103.     set_circle_buf(mesg_info, (int) flags.msg_history);
  104.  
  105.     /* Create a popup that becomes the parent. */
  106.     if (create_popup) {
  107.     num_args = 0;
  108.     XtSetArg(args[num_args], XtNallowShellResize, True); num_args++;
  109.  
  110.     wp->popup = parent = XtCreatePopupShell("message_popup",
  111.                     topLevelShellWidgetClass,
  112.                     toplevel, args, num_args);
  113.     /*
  114.      * If we're here, then this is an auxiliary message window.  If we're
  115.      * cancelled via a delete window message, we should just pop down.
  116.      */
  117.     }
  118.  
  119.     /*
  120.      * Create the viewport.  We only want the vertical scroll bar ever to be
  121.      * visible.  If we allow the horizontal scrollbar to be visible it will
  122.      * always be visible, due to the stupid way the Athena viewport operates.
  123.      */
  124.     num_args = 0;
  125.     XtSetArg(args[num_args], XtNallowVert,  True);      num_args++;
  126.     viewport = XtCreateManagedWidget(
  127.             "mesg_viewport",    /* name */
  128.             viewportWidgetClass,    /* widget class from Window.h */
  129.             parent,            /* parent widget */
  130.             args,            /* set some values */
  131.             num_args);        /* number of values to set */
  132.  
  133.     /*
  134.      * Create a message window.  We will change the width and height once
  135.      * we know what font we are using.
  136.      */
  137.     num_args = 0;
  138.     wp->w = XtCreateManagedWidget(
  139.         "message",        /* name */
  140.         windowWidgetClass,    /* widget class from Window.h */
  141.         viewport,        /* parent widget */
  142.         args,            /* set some values */
  143.         num_args);        /* number of values to set */
  144.  
  145.     XtAddCallback(wp->w, XtNexposeCallback, mesg_exposed, (XtPointer) 0);
  146.  
  147.     /*
  148.      * Now adjust the height and width of the message window so that it
  149.      * is DEFAULT_LINES_DISPLAYED high and DEFAULT_MESSAGE_WIDTH wide.
  150.      */
  151.  
  152.     /* Get the font information. */
  153.     num_args = 0;
  154.     XtSetArg(args[num_args], XtNfont, &mesg_info->fs);           num_args++;
  155.     XtGetValues(wp->w, args, num_args);
  156.  
  157.     /* Save character information for fast use later. */
  158.     mesg_info->char_width    = mesg_info->fs->max_bounds.width;
  159.     mesg_info->char_height   = mesg_info->fs->max_bounds.ascent +
  160.                         mesg_info->fs->max_bounds.descent;
  161.     mesg_info->char_ascent   = mesg_info->fs->max_bounds.ascent;
  162.     mesg_info->char_lbearing = -mesg_info->fs->min_bounds.lbearing;
  163.  
  164.     get_gc(wp->w, mesg_info);
  165.  
  166.     wp->pixel_height = ((int)flags.msg_history) * mesg_info->char_height;
  167.  
  168.     /* If a variable spaced font, only use 2/3 of the default size */
  169.     if (mesg_info->fs->min_bounds.width != mesg_info->fs->max_bounds.width) {
  170.     wp->pixel_width  = ((2*DEFAULT_MESSAGE_WIDTH)/3) *
  171.                     mesg_info->fs->max_bounds.width;
  172.     } else
  173.     wp->pixel_width  = (DEFAULT_MESSAGE_WIDTH *
  174.                     mesg_info->fs->max_bounds.width);
  175.  
  176.     /* Set the new width and height. */
  177.     num_args = 0;
  178.     XtSetArg(args[num_args], XtNwidth,        wp->pixel_width);  num_args++;
  179.     XtSetArg(args[num_args], XtNheight,       wp->pixel_height); num_args++;
  180.     XtSetValues(wp->w, args, num_args);
  181.  
  182.     /* make sure viewport height makes sense before realizing it */
  183.     num_args = 0;
  184.     mesg_info->viewport_height =
  185.     DEFAULT_LINES_DISPLAYED * mesg_info->char_height;
  186.     XtSetArg(args[num_args], XtNheight, mesg_info->viewport_height);num_args++;
  187.     XtSetValues(viewport, args, num_args);
  188.  
  189.     XtAddCallback(wp->w, XtNresizeCallback, mesg_resized, (XtPointer) 0);
  190.  
  191.     /*
  192.      * If we have created our own popup, then realize it so that the
  193.      * viewport is also realized.
  194.      */
  195.     if (create_popup) {
  196.     XtRealizeWidget(wp->popup);
  197.     XSetWMProtocols(XtDisplay(wp->popup), XtWindow(wp->popup),
  198.             &wm_delete_window, 1);
  199.     }
  200. }
  201.  
  202.  
  203. void
  204. destroy_message_window(wp)
  205.     struct xwindow *wp;
  206. {
  207.     if (wp->popup) {
  208.     nh_XtPopdown(wp->popup);
  209.     XtDestroyWidget(wp->popup);
  210.     set_circle_buf(wp->mesg_information, 0);    /* free buffer list */
  211.     free((char *)wp->mesg_information);
  212.     }
  213.     wp->type = NHW_NONE;
  214. }
  215.  
  216.  
  217. /* Redraw message window if new lines have been added. */
  218. void
  219. display_message_window(wp)
  220.     struct xwindow *wp;
  221. {
  222.     if (wp->mesg_information->dirty) redraw_message_window(wp);
  223. }
  224.  
  225.  
  226. /*
  227.  * Append a line of text to the message window.  Split the line if the
  228.  * rendering of the text is too long for the window.
  229.  */
  230. void
  231. append_message(wp, str)
  232.     struct xwindow *wp;
  233.     const char *str;
  234. {
  235.     char *mark, *remainder, buf[BUFSZ];
  236.  
  237.     if (!str) return;
  238.  
  239.     Strcpy(buf, str);    /* we might mark it up */
  240.  
  241.     remainder = buf;
  242.     do {
  243.     mark = remainder;
  244.     remainder = split(mark, wp->mesg_information->fs, wp->pixel_width);
  245.     add_line(wp->mesg_information, mark);
  246.     } while (remainder);
  247. }
  248.  
  249. /* private functions ======================================================= */
  250.  
  251. /*
  252.  * Return the element in the circular linked list just before the given
  253.  * element.
  254.  */
  255. static struct line_element *
  256. get_previous(mark)
  257.     struct line_element *mark;
  258. {
  259.     struct line_element *curr;
  260.  
  261.     if (!mark) return (struct line_element *) 0;
  262.  
  263.     for (curr = mark; curr->next != mark; curr = curr->next)
  264.     ;
  265.     return curr;
  266. }
  267.  
  268.  
  269. /*
  270.  * Set the information buffer size to count lines.  We do this by creating
  271.  * a circular linked list of elements, each of which represents a line of
  272.  * text.  New buffers are created as needed, old ones are freed if they
  273.  * are no longer used.
  274.  */
  275. static void
  276. set_circle_buf(mesg_info, count)
  277.     struct mesg_info_t *mesg_info;
  278.     int count;
  279. {
  280.     int i;
  281.     struct line_element *tail, *curr, *head;
  282.  
  283.     if (count < 0) panic("set_circle_buf: bad count [= %d]", count);
  284.     if (count == mesg_info->num_lines) return;    /* no change in size */
  285.  
  286.     if (count < mesg_info->num_lines) {
  287.     /*
  288.      * Toss num_lines - count line entries from our circular list.
  289.      *  
  290.      * We lose lines from the front (top) of the list.  We _know_
  291.      * the list is non_empty.
  292.      */
  293.     tail = get_previous(mesg_info->head);
  294.     for (i = mesg_info->num_lines - count;    i--; ) {
  295.         curr = mesg_info->head;
  296.         mesg_info->head = curr->next;
  297.         if (curr->line) free(curr->line);
  298.         free((genericptr_t)curr);
  299.     }
  300.     if (count == 0) {
  301.         /* make sure we don't have a dangling pointer */
  302.         mesg_info->head = (struct line_element *) 0;
  303.     } else {
  304.         tail->next = mesg_info->head;    /* link the tail to the head */
  305.     }
  306.     } else {
  307.     /*
  308.      * Add count - num_lines blank lines to the head of the list.
  309.      *
  310.      * Create a separate list, keeping track of the tail.
  311.      */
  312.     for (head = tail = 0, i = 0; i < count - mesg_info->num_lines; i++) {
  313.         curr = (struct line_element *) alloc(sizeof(struct line_element));
  314.         curr->line = 0;
  315.         curr->buf_length = 0;
  316.         curr->str_length = 0;
  317.         if (tail) {
  318.         tail->next = curr;
  319.         tail = curr;
  320.         } else {
  321.         head = tail = curr;
  322.         }
  323.     }
  324.     /*
  325.      * Complete the circle by making the new tail point to the old head
  326.      * and the old tail point to the new head.  If our line count was
  327.      * zero, then make the new list circular.
  328.      */
  329.     if (mesg_info->num_lines) {
  330.         curr = get_previous(mesg_info->head);/* get end of old list */
  331.  
  332.         tail->next = mesg_info->head;    /* new tail -> old head */
  333.         curr->next = head;            /* old tail -> new head */
  334.     } else {
  335.         tail->next = head;
  336.     }
  337.     mesg_info->head = head;
  338.     }
  339.  
  340.     mesg_info->num_lines = count;
  341.     /* Erase the line on a resize. */
  342.     mesg_info->last_pause = (struct line_element *) 0;
  343. }
  344.  
  345.  
  346. /*
  347.  * Make sure the given string is shorter than the given pixel width.  If
  348.  * not, back up from the end by words until we find a place to split.
  349.  */
  350. static char *
  351. split(s, fs, pixel_width)
  352.     char *s;
  353.     XFontStruct *fs;        /* Font for the window. */
  354.     Dimension pixel_width;
  355. {
  356.     char save, *end, *remainder;
  357.  
  358.     save = '\0';
  359.     remainder = 0;
  360.     end = eos(s);    /* point to null at end of string */
  361.  
  362.     /* assume that if end == s, XXXXXX returns 0) */
  363.     while ((Dimension) XTextWidth(fs, s, (int) strlen(s)) > pixel_width) {
  364.     *end-- = save;
  365.     while (*end != ' ') {
  366.         if (end == s) panic("split: eos!");
  367.         --end;
  368.     }
  369.     save = *end;
  370.     *end = '\0';
  371.     remainder = end + 1;
  372.     }
  373.     return remainder;
  374. }
  375.  
  376. /*
  377.  * Add a line of text to the window.  The first line in the curcular list
  378.  * becomes the last.  So all we have to do is copy the new line over the
  379.  * old one.  If the line buffer is too small, then allocate a new, larger
  380.  * one.
  381.  */
  382. static void
  383. add_line(mesg_info, s)
  384.     struct mesg_info_t *mesg_info;
  385.     const char *s;
  386. {
  387.     register struct line_element *curr = mesg_info->head;
  388.     register int new_line_length = strlen(s);
  389.  
  390.     if (new_line_length + 1 > curr->buf_length) {
  391.     if (curr->line) free(curr->line);    /* free old line */
  392.  
  393.     curr->buf_length = new_line_length + 1;
  394.     curr->line = (char *) alloc(curr->buf_length);
  395.     }
  396.  
  397.     Strcpy(curr->line, s);            /* copy info */
  398.     curr->str_length = new_line_length;        /* save string length */
  399.  
  400.     mesg_info->head = mesg_info->head->next;    /* move head to next line */
  401.     mesg_info->dirty = True;            /* we have undrawn lines */
  402. }
  403.  
  404.  
  405. /*
  406.  * Save a position in the text buffer so we can draw a line to seperate
  407.  * text from the last time this function was called.
  408.  *
  409.  * Save the head position, since it is the line "after" the last displayed
  410.  * line in the message window.  The window redraw routine will draw a
  411.  * line above this saved pointer.
  412.  */
  413. void
  414. set_last_pause(wp)
  415.     struct xwindow *wp;
  416. {
  417.     register struct mesg_info_t *mesg_info = wp->mesg_information;
  418.  
  419. #ifdef ERASE_LINE
  420.     /*
  421.      * If we've erased the pause line and haven't added any new lines,
  422.      * don't try to erase the line again.
  423.      */
  424.     if (!mesg_info->last_pause
  425.                 && mesg_info->last_pause_head == mesg_info->head)
  426.     return;
  427.  
  428.     if (mesg_info->last_pause == mesg_info->head) {
  429.     /* No new messages in last turn.  Redraw window to erase line. */
  430.     mesg_info->last_pause = (struct line_element *) 0;
  431.     mesg_info->last_pause_head = mesg_info->head;
  432.     redraw_message_window(wp);
  433.     } else {
  434. #endif
  435.     mesg_info->last_pause = mesg_info->head;
  436. #ifdef ERASE_LINE
  437.     }
  438. #endif
  439. }
  440.  
  441.  
  442. static void
  443. redraw_message_window(wp)
  444.     struct xwindow *wp;
  445. {
  446.     struct mesg_info_t *mesg_info = wp->mesg_information;
  447.     register struct line_element *curr;
  448.     register int row, y_base;
  449.  
  450.     /*
  451.      * Do this the cheap and easy way.  Clear the window and just redraw
  452.      * the whole thing.
  453.      *
  454.      * This could be done more effecently with one call to XDrawText() instead
  455.      * of many calls to XDrawString().  Maybe later.
  456.      *
  457.      * Only need to clear if window has new text.
  458.      */
  459.     if (mesg_info->dirty) {
  460.     XClearWindow(XtDisplay(wp->w), XtWindow(wp->w));
  461.     mesg_info->line_here = mesg_info->last_pause;
  462.     }
  463.  
  464.     /* For now, just update the whole shootn' match. */
  465.     for (y_base = row = 0, curr = mesg_info->head;
  466.         row < mesg_info->num_lines;
  467.         row++, y_base += mesg_info->char_height, curr = curr->next) {
  468.  
  469.     XDrawString(XtDisplay(wp->w), XtWindow(wp->w),
  470.         mesg_info->gc,
  471.         mesg_info->char_lbearing,
  472.         mesg_info->char_ascent + y_base,
  473.         curr->line,
  474.         curr->str_length);
  475.     /*
  476.      * This draws a line at the _top_ of the line of text pointed to by
  477.      * mesg_info->last_pause.
  478.      */
  479.     if (appResources.message_line && curr == mesg_info->line_here) {
  480.         XDrawLine(XtDisplay(wp->w), XtWindow(wp->w),
  481.         mesg_info->gc,
  482.         0, y_base, wp->pixel_width, y_base);
  483.     }
  484.     }
  485.  
  486.     mesg_info->dirty = False;
  487. }
  488.  
  489.  
  490. /*
  491.  * Check the size of the viewport.  If it has shrunk, then we want to
  492.  * move the vertical slider to the bottom.
  493.  */
  494. static void
  495. mesg_check_size_change(wp)
  496.     struct xwindow *wp;
  497. {
  498.     struct mesg_info_t *mesg_info = wp->mesg_information;
  499.     Arg arg[2];
  500.     Dimension new_width, new_height;
  501.     Widget viewport;
  502.  
  503.     viewport = XtParent(wp->w);
  504.  
  505.     XtSetArg(arg[0], XtNwidth,  &new_width);
  506.     XtSetArg(arg[1], XtNheight, &new_height);
  507.     XtGetValues(viewport, arg, TWO);
  508.  
  509.     /* Only move slider to bottom if new size is smaller. */
  510.     if (new_width < mesg_info->viewport_width
  511.             || new_height < mesg_info->viewport_height) {
  512.     set_message_slider(wp);
  513.     }
  514.  
  515.     mesg_info->viewport_width = new_width;
  516.     mesg_info->viewport_height = new_height;
  517. }
  518.  
  519.  
  520. /* Event handler for message window expose events. */
  521. /*ARGSUSED*/
  522. static void
  523. mesg_exposed(w, client_data, widget_data)
  524.     Widget w;
  525.     XtPointer client_data;    /* unused */
  526.     XtPointer widget_data;    /* expose event from Window widget */
  527. {
  528.     XExposeEvent *event = (XExposeEvent *) widget_data;
  529.  
  530.     if (XtIsRealized(w) && event->count == 0) {
  531.     struct xwindow *wp;
  532.     Display *dpy;
  533.     Window   win;
  534.     XEvent   evt;
  535.  
  536.     /*
  537.      * Drain all pending expose events for the message window;
  538.      * we'll redraw the whole thing at once.
  539.      */
  540.     dpy = XtDisplay(w);
  541.     win = XtWindow(w);
  542.     while (XCheckTypedWindowEvent(dpy, win, Expose, &evt)) continue;
  543.  
  544.     wp = find_widget(w);
  545.     mesg_check_size_change(wp);
  546.     redraw_message_window(wp);
  547.     }
  548. }
  549.  
  550.  
  551. static void
  552. get_gc(w, mesg_info)
  553.     Widget w;
  554.     struct mesg_info_t *mesg_info;
  555. {
  556.     XGCValues values;
  557.     XtGCMask mask = GCFunction | GCForeground | GCBackground | GCFont;
  558.     Pixel fgpixel, bgpixel;
  559.     Arg arg[2];
  560.  
  561.     XtSetArg(arg[0], XtNforeground, &fgpixel);
  562.     XtSetArg(arg[1], XtNbackground, &bgpixel);
  563.     XtGetValues(w, arg, TWO);
  564.  
  565.     values.foreground = fgpixel;
  566.     values.background = bgpixel;
  567.     values.function   = GXcopy;
  568.     values.font       = WindowFont(w);
  569.     mesg_info->gc = XtGetGC(w, mask, &values);
  570. }
  571.  
  572. /*
  573.  * Handle resizes on a message window.  Correct saved pixel height and width.
  574.  * Adjust circle buffer to accomidate the new size.
  575.  * 
  576.  * Problem:  If the resize decreases the width of the window such that
  577.  * some lines are now longer than the window, they will be cut off by
  578.  * X itself.  All new lines will be split to the new size, but the ends
  579.  * of the old ones will not be seen again unless the window is lengthened.
  580.  * I don't deal with this problem because it isn't worth the trouble.
  581.  */
  582. /* ARGSUSED */
  583. static void
  584. mesg_resized(w, client_data, call_data)
  585.     Widget w;
  586.     XtPointer call_data, client_data;
  587. {
  588.     Arg args[4];
  589.     Cardinal num_args;
  590.     Dimension pixel_width, pixel_height;
  591.     struct xwindow *wp;
  592. #ifdef VERBOSE
  593.     int old_lines;
  594.  
  595.     old_lines = wp->mesg_information->num_lines;;
  596. #endif
  597.  
  598.     num_args = 0;
  599.     XtSetArg(args[num_args], XtNwidth,  &pixel_width);   num_args++;
  600.     XtSetArg(args[num_args], XtNheight, &pixel_height);  num_args++;
  601.     XtGetValues(w, args, num_args);
  602.  
  603.     wp = find_widget(w);
  604.     wp->pixel_width  = pixel_width;
  605.     wp->pixel_height = pixel_height;
  606.  
  607.     set_circle_buf(wp->mesg_information,
  608.             (int) pixel_height / wp->mesg_information->char_height);
  609.  
  610. #ifdef VERBOSE
  611.     printf("Message resize.  Pixel: width = %d, height = %d;  Lines: old = %d, new = %d\n", 
  612.     pixel_width,
  613.     pixel_height,
  614.     old_lines,
  615.     wp->mesg_information->num_lines);
  616. #endif
  617. }
  618.